
/* GCSx
** FILE.H
**
** Basic file access with exceptions
*/

/*****************************************************************************
** Copyright (C) 2003-2006 Janson
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
** 
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
*****************************************************************************/

#ifndef __GCSx_FILE_H_
#define __GCSx_FILE_H_

// Traits of the current file system

// Case sensitive?
// Uses drives? and separator
// Absolute path prefix
// Valid filename separators
// Preferred filename separator
// Extension separator

#ifdef WIN32

// Windows
#define FILESYSTEM_CASE_SENSITIVE 0
#define FILESYSTEM_DRIVE_SEP ':'
#define FILESYSTEM_ABSOLUTEPREFIX ""
#define FILESYSTEM_SEPARATORS "\\/"
#define FILESYSTEM_PREFERREDSEP "\\"
#define FILESYSTEM_EXTENSIONSEP "."

#else

// For now, assume *nix
#define FILESYSTEM_CASE_SENSITIVE 1
#define FILESYSTEM_DRIVE_SEP 0
#define FILESYSTEM_ABSOLUTEPREFIX "/"
#define FILESYSTEM_SEPARATORS "/"
#define FILESYSTEM_PREFERREDSEP "/"
#define FILESYSTEM_EXTENSIONSEP "."

#endif

// Even game module uses read and write, so there's no need
// for an object that doesn't support writing at all.

class File {
public:
    enum {
        FILE_MODE_READ = 0,
        FILE_MODE_READWRITE,
        FILE_MODE_OVERWRITE, // Read/Write
    };

private:
    FILE* filePtr;
    const char* filename;
    int mode;
    int offset;
    enum {
        OPER_READ,
        OPER_WRITE,
        OPER_SEEK,
    } lastOperation;
    
    void reseek();
    
    enum {
        COPY_BUFFER_SIZE = 8192,
    };
    static char copyBuffer[COPY_BUFFER_SIZE + 1];

public:
    // Pointer must remain valid
    File(const char* fFilename, int fMode) throw_File;
    ~File();

    void read(void* ptr, int size) throw_File;
    Uint32 readInt() throw_File;
    /* not used at this time
    Uint16 readInt16() throw_File;
    // (returns total bytes read- not the same as string length)
    // NON-REENTRANT
    int readStr(std::string& str) throw_File;
    */

    void write(const void* ptr, int size) throw_File;
    void writeInt(Uint32 value) throw_File;
    /* not used at this time
    void writeInt16(Uint16 value) throw_File;
    // (returns total bytes written- not the same as string length)
    int writeStr(const std::string& str) throw_File;
    */
    void writeBlanks(int size) throw_File;
    void flush() throw_File;
    
    // NON-REENTRANT
    void copy(File* copyFrom, int size) throw_File;

    void skip(int size) throw_File;
    void seek(int position) throw_File;
    int tell() const;
};

// Generic filename and directory functions, to keep things cross-compatible

// Top-level directory to start all searching at
const char* topDir();

// Adds to a vector all subdirectories (NULL match) OR matching files (.ext) in a given directory
// Doesn't return full pathnames- just filenames within the dir
// Returns number of matches found
int findFiles(const char* rootdir, const char* match, std::vector<std::string>& result);

// Like findFiles, but merely tells us whether at least one match exists
int existFiles(const char* rootdir, const char* match);

// Pass a rootdir and a subdir name from findFiles to create a new rootdir
// The existence of the directories is not checked
void createDirname(const char* rootdir, const char* subdir, std::string& newRootdir);

// Pass a rootdir and a matching file name from findFiles to create a
// A manually entered filename (relative or absolute path) can also be passed
// filename ready to pass to a File object
// The existence of the directories or file is not checked
void createFilename(const char* rootdir, const char* filename, std::string& fullFilename);

// Pass a filename from createFilename or a filename entered by the user to break it up
// into zero or more rootdirs (subdirectories) and a file name; rootdirs are added in
// order to a vector; the last one is going to either be a file name or a rootdir
// The existence of the directories or file is not checked
void splitFilename(const char* filename, std::vector<std::string>& result);

// Pass a path/filename and it removes the file, returning the pathname
void getPathname(const char* filename, std::string& result);

// Our main directory and resource directory
extern std::string* mainDir;
extern std::string* resourceDir;

#endif

